package ro.ubbcluj.cs.damate.managers.resources;

import org.apache.commons.lang.StringUtils;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import ro.ubbcluj.cs.damate.persistence.entities.Event;
import ro.ubbcluj.cs.damate.persistence.entities.File;
import ro.ubbcluj.cs.damate.persistence.entities.ObservedObject;
import ro.ubbcluj.cs.damate.persistence.entities.Project;
import ro.ubbcluj.cs.damate.persistence.entities.Task;
import ro.ubbcluj.cs.damate.persistence.entities.Token;
import ro.ubbcluj.cs.damate.persistence.entities.User;
import ro.ubbcluj.cs.damate.persistence.entities.Workspace;
import ro.ubbcluj.cs.damate.services.EventService;
import ro.ubbcluj.cs.damate.services.ObservedObjectService;
import ro.ubbcluj.cs.damate.services.TaskService;
import ro.ubbcluj.cs.damate.services.TokenService;
import ro.ubbcluj.cs.damate.services.UserService;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Lóránd Fazakas (lfazakas)
 *         Date: 2013.06.30.
 *         Time: 12:00
 */
@Component
@Scope("view")
public class ResourcesView implements Serializable {

    @Autowired
    private UserService userService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private ObservedObjectService observedObjectService;

    @Autowired
    private EventService eventService;

    @Autowired
    private TaskService taskService;

    private TreeNode root;

    private TreeNode selectedNode;

    private User user;
    private String ipAddress;
    private Workspace workspace;
    private Task selectedTaskToAssign;

    private List<User> activeUsers;
    private List<Task> tasks;
    private List<Event> events;

    public List<User> getActiveUsers() {
        if (activeUsers == null) {
            this.activeUsers = userService.findAllByState(true);
        }
        return activeUsers;
    }

    public List<String> getAvailableIPAddresses() {
        if (user != null) {
            List<String> ipAddresses = new ArrayList<String>();
            for(Token token : tokenService.findByUser(user)){
                ipAddresses.add(token.getIpAddress());
            }
            return ipAddresses;
        }
        return null;
    }

    public List<Workspace> getAvailableWorkspaces() {
        if (user != null && StringUtils.isNotEmpty(ipAddress)) {
            return observedObjectService.findWorkspaceByUserAndIP(user, ipAddress);
        }
        return null;
    }

    public List<Task> getAvailableTasks() {
        if (this.tasks == null && selectedNode != null) {
            this.tasks = taskService.findByUser(user);
        }
        return tasks;
    }

    public void refreshTree() {
        this.root = null;
        this.selectedNode = null;
        this.workspace = observedObjectService.loadWorkspace(workspace);
    }

    public void showEvents() {
        this.events = eventService.findEventByFiles(getAffectedFiles());
    }

    public void assignTask() {
        selectedTaskToAssign = taskService.loadTaskWithRelatedObjects(selectedTaskToAssign);
        for(File file : getAffectedFiles()) {
            if (!alreadyAssigned(selectedTaskToAssign.getAssignedObjects(), file)) {
                selectedTaskToAssign.getAssignedObjects().add(file);
            }
        }
        taskService.update(selectedTaskToAssign);
    }

    private boolean alreadyAssigned(List<ObservedObject> assignedResources, ObservedObject newResource) {
        for(ObservedObject assignedObject : assignedResources) {
            if (assignedObject.getObservedEntityId().equals(newResource.getObservedEntityId())) {
                return true;
            }
        }
        return false;
    }

    public List<Event> getSelectedResourceEvents() {
        return events;
    }

    private List<File> getAffectedFiles() {
        List<File> files = new ArrayList<File>();
        if (selectedNode.getType().equals("project")) {
            files = ((Project)selectedNode.getData()).getFiles();
        } else {
            TreeNode currentNode = selectedNode;
            StringBuilder path = new StringBuilder();
            while(!currentNode.getType().equals("project")) {
                path.insert(0, "/"+currentNode.getData().toString());
                currentNode = currentNode.getParent();
            }
            for(File file : ((Project)currentNode.getData()).getFiles()) {
                if (file.getPath().startsWith(path.substring(1))) {
                    files.add(file);
                }
            }
        }
        return files;
    }

    public TreeNode getTreeRoot() {
        if (root == null) {
            this.root = new DefaultTreeNode("root", null);
            if (workspace == null) {
                new DefaultTreeNode("empty", null, root);
            } else {
                for(Project project : workspace.getProjects()) {
                    buildProjectTree(new DefaultTreeNode("project", project, root), project.getFiles());
                }
            }
            sort(root);
        }
        return root;
    }

    private void sort(TreeNode root) {
        for(TreeNode project : root.getChildren()) {
            Collections.sort(project.getChildren(), new ResourceComparator());
        }
    }

    private void buildProjectTree(TreeNode projectNode, List<File> files) {
        ResourceTree projectRoot = new ResourceTree(projectNode);
        ResourceTree current = projectRoot;
        List<String> paths = new ArrayList<String>();
        for(File file : files) {
            ResourceTree root = current;
            String[] pathParts = file.getPath().split("[\\/]+");
            int index = 0;
            for (String resource : pathParts) {
                current = current.child(resource, file.getPath(), file.getType(), index == 0, index == pathParts.length-1);
                index++;
            }
            current = root;
        }
    }

    public TreeNode getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(TreeNode selectedNode) {
        this.selectedNode = selectedNode;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
        this.ipAddress = null;
        this.workspace = null;
        this.root = null;
        this.selectedNode = null;
        this.tasks = null;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
        this.workspace = null;
        this.root = null;
        this.selectedNode = null;
        this.tasks = null;
    }

    public Workspace getWorkspace() {
        return workspace;
    }

    public void setWorkspace(Workspace workspace) {
        this.workspace = workspace;
        this.root = null;
    }

    public Task getSelectedTaskToAssign() {
        return selectedTaskToAssign;
    }

    public void setSelectedTaskToAssign(Task selectedTaskToAssign) {
        this.selectedTaskToAssign = selectedTaskToAssign;
    }

    public Converter getUserConverter() {
        return new Converter() {
            @Override
            public Object getAsObject(FacesContext context, UIComponent component, String value) {
                for(User user : getActiveUsers()) {
                    if (user.getUsername().equals(value)) {
                        return user;
                    }
                }
                return null;
            }

            @Override
            public String getAsString(FacesContext context, UIComponent component, Object value) {
                return value.toString();
            }
        };
    }

    public Converter getWorkspaceConverter() {
        return new Converter() {
            @Override
            public Object getAsObject(FacesContext context, UIComponent component, String value) {
                for(Workspace workspace : getAvailableWorkspaces()) {
                    if (workspace.getObservedEntityId().toString().equals(value)) {
                        return workspace;
                    }
                }
                return null;
            }

            @Override
            public String getAsString(FacesContext context, UIComponent component, Object value) {
                return value instanceof Workspace ? ((Workspace)value).getObservedEntityId().toString() : value.toString();
            }
        };
    }

    public Converter getTaskConverter() {
        return new Converter() {
            @Override
            public Object getAsObject(FacesContext context, UIComponent component, String value) {
                for(Task task : getAvailableTasks()) {
                    if (task.getTaskId().toString().equals(value)) {
                        return task;
                    }
                }
                return null;
            }

            @Override
            public String getAsString(FacesContext context, UIComponent component, Object value) {
                return value instanceof Task ? ((Task)value).getTaskId().toString() : value.toString();
            }
        };
    }
}
